#include "eval-upmc.h"

void free_ast(ast_st * ast)
{
  
  switch(ast->kind){

  case Nothing :
    break;

  case Plus :
    free_ast(ast->left);
    free_ast(ast->right);
    break;

  case Minus :
    free_ast(ast->left);
    free_ast(ast->right);
    break;

  case Mult :
    free_ast(ast->left);
    free_ast(ast->right);
    break;

  case Div :
    free_ast(ast->left);
    free_ast(ast->right);
    break;

  case Integer :
    break;

  case Real :
    free(ast);
    break;

  default :
    break;
  }
}

ast_st * create_ast(const char *arith_expr)
{
  ast_st * ast = NULL;
  ast_st * end_node = NULL;
  ast_st * left = NULL;
  ast_st * right = NULL;
  int isReal = 0;
  
  //Char pointer 
  const char *ptchar = arith_expr;
  const char *svchar = ptchar;

  //Allocation memory of the structures
  ast = (ast_st*)malloc(sizeof(ast_st));
  left = (ast_st*)malloc(sizeof(ast_st));
  right = (ast_st*)malloc(sizeof(ast_st));
  
  if(left == NULL){
    fprintf(stderr,"Allocation memory of left failed.\n");
  }

  if(right == NULL){
    fprintf(stderr,"Allocation memory of right failed.\n");
  }

  if(ast == NULL){
    fprintf(stderr,"Allocation memory of right failed.\n");
  }

  //
  while(*ptchar != '\0'){

    if(isdigit(*ptchar))
      {
	//Case where the char is a number
	left->right = NULL;
	left->left = NULL;
	
	svchar = ptchar;

	while(*ptchar){
	  if(*ptchar == '.')
	    isReal = 1;
	  ptchar++;
	}

	if(isReal)
	  {
	    left->kind = Real;
	    left->num = atof(svchar);
	  }
	else
	  {
	    left->kind = Integer;
	    left->num = atoi(svchar);
	  }
      }
    else
      {
	//Case where the char represents an operator
	right->left = NULL;
	right->right = NULL;
	
	switch(*ptchar){
	case '+' :
	  right->kind = Plus;
	  break;
	case '-' :
	  right->kind = Minus;
	  break;
	case '*' :
	  right->kind = Mult;
	  break;
	case '/' :
	  right->kind = Div;
	  break;
	default :
	  //Unreachable case
	  fprintf(stderr,"Operator unknown.\n");
	  break;
	}

	//Priority
	if (right->kind == Plus || right->kind == Minus)
	  {
	    if (ast == NULL)
	      {
		ast = right;
		ast->left = left;
		end_node = ast;
	      }
	    else
	      {
		end_node->right = left;
		right->left = ast;
		end_node = ast = right;
	      }
	  }
	else
	  {
	    if (ast == NULL)
	      {
		ast = right;
		ast->left = left;
		end_node = ast;                  
	      }
	    else
	      {
		end_node->right = right;
		right->left = left;
		end_node = right;
	      }
	  }
      }
    ptchar++;
    //end while
  }
  return ast;
}

ast_st * create_node(kind_en op, ast_st * left, ast_st * right)
{
  ast_st *a = (ast_st*) malloc(sizeof(ast_st));

  if (a == NULL) {
    free_ast(right);
    free_ast(left);
    fprintf(stderr,"AST creation failed.\n");
  }
    
  a->kind = op;
  a->right = right;
  a->left = left;

  return a;
}

ast_st * create_node_int(int num)
{
  ast_st *ast;
  ast = (ast_st*) malloc(sizeof(ast_st));
  if (ast == NULL) {
    fprintf(stderr,"AST node integer creation failed.\n");
    exit(1);
  }
  ast->kind = Integer;
  ast->num = num;
  return ast;
}

ast_st * create_node_real(double flo)
{
  ast_st *ast;
  ast = (ast_st*) malloc(sizeof(ast_st));
  if (ast == NULL) {
    fprintf(stderr,"AST node double creation failed.\n");
    exit(1);
  }
  ast->kind = Real;
  ast->flo = flo;
  return ast;
}


double eval_ast(ast_st * ast)
{
  double val_right =0.0;
  double val_left =0.0;
  switch (ast->kind) {
  case Real:
    return ast->flo;
    break;
  case Integer:
    return (double) ast->num;
    break;
  case Plus:
    return  eval_ast(ast->left)+ eval_ast(ast->right);
    break;
  case Minus:
    return eval_ast(ast->left)-eval_ast(ast->right);
    break;
  case Mult:
    return eval_ast(ast->left)*eval_ast(ast->right);
    break;
  case Div:
    val_right = eval_ast(ast->right);
    val_left = eval_ast(ast->left);
    if (val_right == 0) {
      fprintf(stderr,"Division by zero\n");
    }
    return val_left/val_right;
    break;

  default:
    return 0.0;
    break;
  }
}
